package com.nextgenapp.wave.gadget
{
	/**
	 * simulate wave state callback 
	 * so that this can be run outside of wave container.  
	 * 
	 * whenever you call submitDelta() or submitValue(), you will get a 
	 * stateCallback right away with all the data submitted.  
	 * 
	 * @author sol wu
	 * 
	 * @version since 0.5
	 * 
	 * 
	 * <code>
	 * 
	 * var wave:WaveSimulator = new WaveSimulator();
	 * wave.setStateCallback(stateCallback);
	 * wave.setParticipantCallback(participantCallback);
	 * 
	 * var delta:Object = {};
	 * delta.test = "test";
	 * wave.submitDelta(delta);
	 * wave.submitValue("test", "testvalue");
	 * var newState:WaveState = wave.getValue();
	 * 
	 * var host:WaveParticipant = new WaveParticipant();
	 * host.id = "testhost@googlewave.com";
	 * host.thumbnailUrl = "http://domain.com/picture.gif";
	 * host.displayName = "test host";
	 * wave.addHost(host);
	 * 
	 * var viewer:WaveParticipant = new WaveParticipant();
	 * viewer.id = "testviewer@googlewave.com";
	 * viewer.thumbnailUrl = "http://domain.com/picture.gif";
	 * viewer.displayName = "test viewer";
	 * wave.addViewer(viewer);
	 * 
	 * var p1:WaveParticipant = new WaveParticipant();
	 * p1.id = "testparticipant1@googlewave.com";
	 * p1.thumbnailUrl = "http://domain.com/picture.gif";
	 * p1.displayName = "test participant 1";
	 * wave.addParticipant(p1);
	 * 
	 * var participants:Array = wave.getParticipants();
	 * 
	 * </code>
	 * 
	 * 
	 * 
	 */
	public class WaveSimulator extends Wave
	{
		/** collects the state data */
		private var stateObj:Object = {};
		
		private var simulatorStateCallback:Function = null;
		
		/** 
		 * collects all the participants added through addParticipant().  
		 * this array will be returned to participantCallback method
		 */
		private var participants:Array = [];
		
		private var simulatorParticipantCallback:Function = null;
		
		private var host:WaveParticipant = null;
		
		private var viewer:WaveParticipant = null;
		
		public function WaveSimulator(...domains)
		{
			super(domains);
		}
		
		/**
		 * @param callback 
		 * @param opt_context	 the object that receives the callback
		 */
		public override function setStateCallback(callback:Function, opt_context:Object=null):void {
			simulatorStateCallback = callback;
			// on wave sandbox, wave.setStateCallback() seems to trigger a state callback.    
			// to simulate the same behavior, we will trigger a statecallback right away. 
			var state:WaveState = WaveState.unboxState(stateObj); 
			if (simulatorStateCallback != null) {
				simulatorStateCallback(state);
			}
		}
		
		public override function setParticipantCallback(callback:Function, opt_context:Object=null):void {
			simulatorParticipantCallback = callback;  
		}
		
		/**
		 * key-value pair in delta object is added to the state.
		 * triggers stateCallback immediately 
		 */
		public override function submitDelta(delta:Object):void {
			for (var propName:Object in delta) {
				stateObj[propName] = delta[propName];
			}
			
			var state:WaveState = WaveState.unboxState(stateObj);
			if (simulatorStateCallback != null) {
				simulatorStateCallback(state);
			}
		}
		
		/**
		 * key-value pair is added to the state.
		 * triggers stateCallback immediately 
		 */
		public override function submitValue(key:String, value:String):void {
			stateObj[key] = value;
			
			var state:WaveState = WaveState.unboxState(stateObj);
			if (simulatorStateCallback != null) {
				simulatorStateCallback(state);
			}
		}
		
		/**
		 * remove all key-values in the state.
		 * triggers stateCallback immediately.  
		 */
		public override function reset():void {
			stateObj = {};
			
			var state:WaveState = WaveState.unboxState(stateObj);
			if (simulatorStateCallback != null) {
				simulatorStateCallback(state);
			}
		}
		
		/**
		 * the state contains whatever data you added using 
		 * submitDelta() or submitValue()
		 */
		public override function getState():WaveState {
			return WaveState.unboxState(stateObj);
		}

		/**
		 * set host.
		 * add host to participants list.  
		 * host will be one of the participants returend by getParticipants().  
		 */ 
		public function setHost(host:WaveParticipant):void {
			this.host = host;
			// only add to participants list if it is not yet in the list
			if (participants.indexOf(host) == -1) {
				participants.push(host);
			}
		}
		
		/**
		 * @return host if setHost() has been called.  null if setHost() is not called.  
		 */
		public override function getHost():WaveParticipant {
			return host;
		}
		
		/**
		 * set viewer.   
		 * add viewer to participants list. 
		 * viewer will be one of the participants returend by getParticipants().   
		 */
		public function setViewer(viewer:WaveParticipant):void {
			this.viewer = viewer;
			// only add viewer to participants list if it is not yet in the list
			if (participants.indexOf(viewer) == -1) {
				participants.push(viewer);
			}
		}
		
		/**
		 * @return viewer if setViewer() has been called.  null if setViewer() is not called.  
		 */
		public override function getViewer():WaveParticipant {
			return viewer;
		}
		
		/**
		 * test method.
		 * this method is not part of the Wave methods.    
		 * allows the test code to add a new participant programmatically.  
		 * triggers participantCallback immediately.  
		 */
		public function addParticipant(participant:WaveParticipant):void {
			participants.push(participant);
			if (simulatorParticipantCallback != null) {
				simulatorParticipantCallback(participants);
			}
		}
				
		/**
		 * return all the participants added using addParticipant(), setViewer(), and setHost().
		 */
		public override function getParticipants():Array {
			return participants;
		}
		
		/**
		 * search for the participant in the list of participants 
		 * added by addParticipant(), setViewer(), and setHost()
		 */
		public override function getParticipantById(id:String):WaveParticipant {
			for each (var participant:WaveParticipant in participants) {
				if (participant.id == id) {
					return participant;
				}
			}
			return null;
		}

		/**
		 * always return true
		 */
		public override function isInWaveContainer():Boolean {
			return true;
		}
		
		/**
		 * return current time
		 */
		public override function getTime():Number {
			return new Date().getTime();
		}
		
		/**
		 * always return false
		 */
		public override function isPlayback():Boolean {
			return false;
		}
	}
}